package frontend.parser;

import frontend.cehandler.compilationerrors.CECause;
import frontend.cehandler.compilationerrors.CEType;
import frontend.cehandler.CEHandler;
import frontend.lexer.Lexer;
import frontend.lexer.token.Token;
import frontend.parser.node.Node;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;

public class ParserSupporter {
    private final Parser parser;
    private int pos = 0;
    private final Lexer lexer;
    private final PrintWriter printWriter;
    private final PrintWriter debugWriter;
    private final CEHandler ceHandler;
    private final ArrayList<Token> tokens;
    private final ArrayList<Integer> tags = new ArrayList<>();
    ParserSupporter(Parser parser) {
        this.parser = parser;
        this.lexer = parser.lexer;
        this.tokens = parser.lexer.getTokens();
        this.printWriter = parser.printWriter;
        this.debugWriter = parser.debugWriter;
        this.ceHandler = parser.ceHandler;
    }

    private boolean update(int pos) {
        do {
            if (tokens.size() > pos) {
                return true;
            }
        } while (lexer.analyseOne() != null);
        return false;
    }
    boolean hasNow() {
        return hasNext(0);
    }
    boolean hasNext() {
        return hasNext(1);
    }
    boolean hasNext(int n) {
        return update(pos + n);
    }
    Token now() {
        return next(0);
    }
    Token next() {
        return next(1);
    }
    Token next(int n) {
        if (!hasNext(n)) {
            return null;
        }
        return tokens.get(pos + n);
    }
    void push() {
        push(1);
    }
    void push(int n) {
        pos += n;
    }
    void printNode(String nodeName) {
        printWriterTarget().println("<" + nodeName + ">");
    }
    void printTokenNode(Token token) {
        printWriterTarget().println(token.toString());
    }
    PrintWriter printWriterTarget() {
        return isTrying() ? parserTry.tryingPrintWriter : this.printWriter;
    }
    void error(CEType errorType, String message, Token causedToken ,Node causedNode) {
        CEHandler ceHandlerTarget = isTrying() ? parserTry.tryingCEHandler : this.ceHandler;
        ceHandlerTarget.parserError(errorType, message, causedToken, causedNode);
    }
    void error(CEType errorType, String message, Node causedNode) {
        error(errorType, message, now(), causedNode);
    }
    boolean isTrying() {
        return parserTry != null;
    }
    void startTrying() {
        parserTry = new ParserTry();
    }
    void endTrying(boolean success) {
        if (success) {
            parserTry.flushAll();
            printWriter.print(parserTry.sw_tryingPrintWriter.toString());
            ceHandler.addErrors(parserTry.tryingCEHandler);
        } else {
            this.pos = parserTry.tagPos;
        }
        parserTry = null;
    }
    private ParserTry parserTry;
    class ParserTry {
        private PrintWriter tryingPrintWriter;
        private StringWriter sw_tryingPrintWriter;
        private CEHandler tryingCEHandler;
        int tagPos;
        ParserTry() {
            tagPos = pos;
            sw_tryingPrintWriter = new StringWriter();
            tryingPrintWriter = new PrintWriter(sw_tryingPrintWriter);
            tryingCEHandler = new CEHandler();
        }
        void flushAll() {
            tryingPrintWriter.flush();
            sw_tryingPrintWriter.flush();
        }

    }
}
